﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data;
using System.Reflection;
using System.Reflection.Emit;
using System.Data.Common;
using System.Collections;
using System.Data.Odbc;
using RgUtility.OrmForOracle.FastSetter;
using Oracle.DataAccess.Client;

namespace RgUtility.OrmForOracle
{

    /// <summary>
    /// 数据库上下文。
    /// 经过多轮优化，不要更改返回List为IList
    /// 
    /// 作者：蒋大为
    /// </summary>
    public class DbContext : IDisposable
    {
        public OracleConnection conn = null;
        private OracleTransaction tran = null;
        private static Type p = typeof(object);

        /// <summary>
        /// 连接字符串
        /// </summary>
        /// <param name="connectionString">连接字符串</param>
        public DbContext(string connectionString)
        {
            conn = new OracleConnection(connectionString);
            conn.Open();
        }
        /// <summary>
        /// 开始一个事务。
        /// </summary>
        /// <param name="level"></param>
        /// <returns></returns>
        public OracleTransaction BeginTransaction(IsolationLevel? level = null)
        {
            if (level == null)
                this.tran = this.conn.BeginTransaction();
            else
                this.tran = this.conn.BeginTransaction(level.Value);
            return tran;
        }
        /// <summary>
        /// from 表实体。
        /// 通过实体的属性拼接crud的Sql语句。
        /// </summary>
        /// <typeparam name="Table">表对应的表实体</typeparam>
        /// <returns></returns>
        public TableContext<Table> From<Table>() where Table : new()
        {
            return new TableContext<Table>(this);
        }

        #region  Ado.net标准方法
        /// <summary>
        /// 查询得到DataTable
        /// </summary>
        /// <param name="sql">sql</param>
        /// <param name="p">参数(可null)</param>
        /// <returns></returns>
        public DataTable Query(string sql, OracleParameter[] p)
        {
            var dsTable = new DataTable();
            using (OracleCommand cmd = new OracleCommand(sql, this.conn))
            {
                cmd.BindByName = true;
                if (p != null && p.Length > 0)
                {
                    cmd.Parameters.AddRange(p);
                }
                if (tran != null)
                {
                    cmd.Transaction = tran;
                }
                #region 不用时注释掉
                System.Diagnostics.Debug.WriteLine(Tool.SqlLogTool.GetExecutedSql(sql, p));
                #endregion
                using (OracleDataAdapter adapter = new OracleDataAdapter(cmd))
                {
                    adapter.Fill(dsTable);
                }
            }
            return dsTable;
        }
        /// <summary>
        /// 查询得到list. 将列填充到实体的同名属性中。
        /// </summary>
        /// <typeparam name="T">填充的类型</typeparam>
        /// <param name="sql">使用的sql</param>
        /// <param name="sqlParaArr">sql参数可null</param>
        /// <returns>List</returns>

        public List<T> Query<T>(string sql, OracleParameter[] sqlParaArr) where T : new()
        {//高性能读写方法不要更改任何代码。
            LinkedList<T> ls = new LinkedList<T>();
            using (OracleCommand cmd = new OracleCommand(sql, this.conn))
            {
                cmd.BindByName = true;
                if (sqlParaArr != null && sqlParaArr.Length > 0)
                {
                    cmd.Parameters.AddRange(sqlParaArr);
                }
                if (tran != null)
                {
                    cmd.Transaction = tran;
                }
                #region 注释的优化过程
                #region 经过测试反射在少量数据时相比委托也没有任何优势
                //if (way == SetWay.Reflection)
                //{
                //    #region
                //    var propLs = typeof(T).GetProperties();
                //    var t = typeof(T);
                //    //Dictionary<string, PropertyInfo> dict = new Dictionary<string, PropertyInfo>();
                //    //foreach (var item in propLs)
                //    //{
                //    //    dict[item.Name] = item;
                //    //}
                //    using (var reader = cmd.ExecuteReader())
                //    {
                //        while (reader.Read())
                //        {
                //            var row = new T();
                //            for (int i = 0; i < reader.FieldCount; i++)
                //            {
                //                var coluName = reader.GetName(i);
                //                t.GetProperty(coluName).SetValue(row, reader.GetValue(i) == System.DBNull.Value ? null : reader.GetValue(i));
                //                //dict[coluName].SetValue(row, reader.GetValue(i) == System.DBNull.Value ? null : reader.GetValue(i));
                //            }
                //            ls.Add(row);
                //        }
                //    }
                //    return ls;
                //    #endregion
                //}
                #endregion
                #region DataTable赋值
                //if (way == SetWay.DataTable)
                //{
                //    #region
                //    using (SqlDataAdapter adapter = new SqlDataAdapter(cmd))
                //    {
                //        using (var dsTable = new DataTable())
                //        {
                //            adapter.Fill(dsTable);
                //            return RgUntility.Common.CommonTool.CollectionTool.ConvertTo<T>(dsTable);
                //        }
                //    }
                //    #endregion
                //}
                #endregion
                #region  公共访问器
                //}
                //else if (way == SetWay.CommonSetter)
                //{
                //    #region
                //    var propLs = typeof(T).GetProperties();
                //    Dictionary<string, ISetValue> dict = new Dictionary<string, ISetValue>();
                //    foreach (var item in propLs)
                //    {
                //        dict[item.Name] = (ISetValue)Class1.CreatePropertySetterWrapper(item);
                //    }
                //    using (var reader = cmd.ExecuteReader())
                //    {
                //        while (reader.Read())
                //        {

                //            var t = new T();
                //            for (int i = 0; i < reader.FieldCount; i++)
                //            {
                //                var coluName = reader.GetName(i);
                //                if (dict[coluName] == null)
                //                {
                //                    throw new ArgumentException("没有找到对应读取器的属性！");
                //                }
                //                var val = reader.GetValue(i);
                //                if (val != System.DBNull.Value)
                //                {
                //                    dict[coluName].Set(t, val);
                //                }
                //            }
                //            ls.Add(t);
                //        }
                //    }
                //    return ls;
                //    #endregion
                //}
                #endregion
                #endregion

                var propArr = typeof(T).GetProperties();
                // var t = typeof(T);
                Dictionary<string, SetValueDelegate> dict_Str_Set = new Dictionary<string, SetValueDelegate>();
                for (int i = 0; i < propArr.Length; i++)
                {
                    dict_Str_Set[propArr[i].Name] = CreatePropertySetter(propArr[i], p);
                }
                Dictionary<int, SetValueDelegate> dict_Int_Set = new Dictionary<int, SetValueDelegate>();

                System.Diagnostics.Debug.WriteLine(Tool.SqlLogTool.GetExecutedSql(sql,sqlParaArr));
                using (var reader = cmd.ExecuteReader())
                {
                    if (reader.Read())
                    {
                        for (int i = 0; i < reader.FieldCount; i++)
                        {
                            var temp = reader.GetName(i);
                            try
                            {
                                dict_Int_Set[i] = dict_Str_Set[temp];
                            }
                            catch (System.Collections.Generic.KeyNotFoundException ex)
                            {
                                throw new Exception("没有" + temp + "属性!");
                            }
                            //dict1[i] = CreatePropertySetter(t.GetProperty(reader.GetName(i)),p);
                        }
                        do
                        {
                            var row = new T();
                            for (int i = 0; i < reader.FieldCount; i++)
                            {
                                var val = reader.GetValue(i);
                                if (val == System.DBNull.Value)
                                {
                                    continue;
                                }
                                try
                                {
                                    dict_Int_Set[i].Invoke(row, val);
                                }
                                catch (System.InvalidCastException ex)
                                {
                                    throw new Exception(reader.GetName(i) + "列的类型转换无效！应为"+val.GetType()+"类型！");
                                }
                                catch (System.Exception ex1)
                                {
                                    throw new Exception(reader.GetName(i) + "列赋值到属性时出错！");
                                }
                            }
                            ls.AddLast(row);
                        } while (reader.Read());
                    }
                }
            }


            return ls.ToList();//转为List提供更好的功能。
        }

        /// <summary>
        /// 返回受影响行数
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="p"></param>
        /// <returns></returns>
        public int NonQuery(string sql, OracleParameter[] p)
        {
            using (OracleCommand cmd = new OracleCommand(sql, this.conn))
            {
                cmd.BindByName = true;
                if (p != null && p.Length > 0)
                {
                    cmd.Parameters.AddRange(p);
                }
                if (tran != null)
                {
                    cmd.Transaction = tran;
                }
                System.Diagnostics.Debug.WriteLine(Tool.SqlLogTool.GetExecutedSql(sql, p));
                return cmd.ExecuteNonQuery();
            }
        }

        #endregion

        public void Dispose()
        {
            if (this.conn != null)
            {
                if (this.conn.State != System.Data.ConnectionState.Closed)
                {
                    this.conn.Close();
                }
            }
        }
        /// <summary>
        /// 专用快速Setter方法！
        /// </summary>
        /// <param name="property"></param>
        /// <param name="p"></param>
        /// <returns></returns>
        private static SetValueDelegate CreatePropertySetter(PropertyInfo property, Type p)
        {
            MethodInfo setMethod = property.GetSetMethod(false);
            DynamicMethod dm = new DynamicMethod("", //方法名
                null,//方法返回值类型
                new Type[] { p, p },//方法参数：target,arg
                property.DeclaringType, //创建的动态方法关联的类。
                true//不做jit访问检查。
                );
            ILGenerator il = dm.GetILGenerator();//得到一个il生成器。
            il.Emit(OpCodes.Ldarg_0);//il发出流代码0。
            il.Emit(OpCodes.Ldarg_1);//il发出流代码1。
            var proptype = property.PropertyType;
            if (proptype.IsValueType)
            {
                il.Emit(OpCodes.Unbox_Any, proptype);//object to value
            }
            else
            {
                il.Emit(OpCodes.Castclass, proptype);//object to T
            }
            il.EmitCall(OpCodes.Callvirt, setMethod, null);//il发出调用虚方法。
            il.Emit(OpCodes.Ret);//返回。
            return (SetValueDelegate)dm.CreateDelegate(typeof(SetValueDelegate));

        }
    }
}
